<template>
	<div class="workflow-container">
		<div class="workflow-mask" v-if="isShow"></div>
		<div class="layout-view-bg-white flex" :style="{ height: `calc(100vh - ${setViewHeight}` }">
			<div class="workflow">
				<!-- 顶部工具栏 -->
				<Tool :model="workableTask" @tool="onToolClick" />

				<!-- 左侧导航区 -->
				<div class="workflow-content">
					<div id="workflow-left">
						<el-scrollbar>
							<div
								:id="`left${key}`"
								v-for="(val, key) in leftNavList"
								:key="val.id"
								:style="{ height: val.isOpen ? 'auto' : '50px', overflow: 'hidden' }"
								class="workflow-left-id"
							>
								<div class="workflow-left-title" @click="onTitleClick(val)">
									<span>{{ val.title }}</span>
									<SvgIcon :name="val.isOpen ? 'ele-ArrowDown' : 'ele-ArrowRight'" />
								</div>
								<div class="workflow-left-item" v-for="(v, k) in val.children" 
									:key="k" 
									:data-type="v.type" 
									:data-name="v.name" 
									:data-icon="v.icon" 
									:data-id="v.id"
									:data-model-id="v.modelId">
									<div class="workflow-left-item-icon">
										<SvgIcon :name="v.icon" class="workflow-icon-drag" />
										<div class="font10 pl5 name">{{ v.name }}</div>
									</div>
								</div>
							</div>
						</el-scrollbar>
					</div>

					<!-- 右侧绘画区 -->
					<div id="workflow-right">
						<div
							v-for="(v, k) in jsplumbData.nodeList"
							:key="v.nodeId"
							:id="v.nodeId"
							:class="v.cssClass"
							:style="{ left: v.left, top: v.top }"
							@click="onItemCloneClick(k)"
							@dblclick="onItemCloneDblClick(v, k)"
							@contextmenu.prevent="onContextmenu(v, k, $event)"
						>
							<div class="workflow-right-box" :class="{ 'workflow-right-active': jsPlumbNodeIndex === k }">
								<div class="workflow-left-item-icon">
									<SvgIcon :name="v.icon" class="workflow-icon-drag" />
									<div class="font10 pl5 name">{{ v.name }}</div>
								</div>
							</div>
						</div>
					</div>
				</div>
			</div>
		</div>

		<!-- 节点右键菜单 -->
		<Contextmenu :dropdown="dropdownNode" ref="contextmenuNodeRef" @current="onCurrentNodeClick" />
		<!-- 线右键菜单 -->
		<Contextmenu :dropdown="dropdownLine" ref="contextmenuLineRef" @current="onCurrentLineClick" />
		<!-- 抽屉表单、线 -->
		<Drawer ref="drawerRef" :readonly="readonly" @label="setLineLabel" @node="setNodeContent" />

		<!-- 顶部工具栏-帮助弹窗 -->
		<Help ref="helpRef" />
		<Text ref="textRef" />
		
		<el-drawer v-model="drawer.isShow" title="运行结果" direction="rtl" size="80%"
			:close-on-click-modal="false"
			:close-on-press-escape="false"
		>
			<ResultWorkableTask ref="resultWorkableTaskRef" :taskId="drawer.taskId" :runmode="drawer.runmode"/>
		</el-drawer>
	</div>
</template>

<script lang="ts">
import { defineComponent, toRefs, reactive, computed, onMounted, onUnmounted, nextTick, ref, getCurrentInstance } from 'vue';
import { ElMessage, ElMessageBox } from 'element-plus';
import { useRoute } from 'vue-router';
import { jsPlumb } from 'jsplumb';
import { nanoid } from 'nanoid';
import Sortable from 'sortablejs';
import { useStore } from '/@/store/index';
import Tool from './component/tool/index.vue';
import Help from './component/tool/help.vue';
import Text from './component/tool/text.vue';
import Contextmenu from './component/contextmenu/index.vue';
import ResultWorkableTask from '/@/views/workable/result/index.vue';
import Drawer from './component/drawer/index.vue';
import { copyText } from '/@/utils/commonFunction';
import { leftNavList } from './js/mock';
import { jsplumbDefaults, jsplumbMakeSource, jsplumbMakeTarget, jsplumbConnect } from './js/config';
import { getWorkableTask } from '/@/api/workable/task/index';
import { delWorkableNode } from '/@/api/workable/node/index';
import { delWorkableLine } from '/@/api/workable/line/index';
import { getWorkableJsPlumbData, saveWorkableJsPlumbData } from '/@/api/workable/index';
import { addWorkableForm, getWorkableForm } from '/@/api/workable/form/index';
import { runWorkableTask } from '/@/api/workable/task/index';

// 定义接口来定义对象的类型
interface NodeListState {
	id: string | number | undefined;
	modelId: string;
	taskId: string | undefined;
	nodeId: string | undefined;
	cssClass: HTMLElement | string;
	left: number | string;
	top: number | string;
	icon: string;
	name: string;
	type: string;
}
interface LineListState {
	id: string | number | undefined;
	taskId: string | undefined;
	sourceId: string;
	targetId: string;
	label: string;
	expression: string | undefined;
}
interface XyState {
	x: string | number;
	y: string | number;
}
interface WorkflowState {
	leftNavList: any[];
	dropdownNode: XyState;
	dropdownLine: XyState;
	isShow: boolean;
	jsPlumb: any;
	jsPlumbNodeIndex: null | number;
	jsplumbDefaults: any;
	jsplumbMakeSource: any;
	jsplumbMakeTarget: any;
	jsplumbConnect: any;
	jsplumbData: {
		nodeList: Array<NodeListState>;
		lineList: Array<LineListState>;
	};
	workableTask: any;
}

export default defineComponent({
	name: 'workable/editor',
	components: { Tool, Contextmenu, Drawer, Help, Text, ResultWorkableTask },
	setup() {
		const { proxy } = getCurrentInstance() as any;
		const route = useRoute();
		const contextmenuNodeRef = ref();
		const contextmenuLineRef = ref();
		const drawerRef = ref();
		const helpRef = ref();
		const textRef = ref();
		const store = useStore();
		const state = reactive<WorkflowState>({
			leftNavList: [],
			dropdownNode: { x: '', y: '' },
			dropdownLine: { x: '', y: '' },
			isShow: false,
			jsPlumb: null,
			jsPlumbNodeIndex: null,
			jsplumbDefaults,
			jsplumbMakeSource,
			jsplumbMakeTarget,
			jsplumbConnect,
			jsplumbData: {
				nodeList: [],
				lineList: [],
			},
			workableTask: {},
			traceId: null,
			readonly: false,
			drawer: {
				isShow: false,
				taskId: '',
				runmode: 0,
			},
		});
		// 设置 view 的高度
		const setViewHeight = computed(() => {
			let { isTagsview } = store.state.themeConfig.themeConfig;
			let { isTagsViewCurrenFull } = store.state.tagsViewRoutes;
			if (isTagsViewCurrenFull) {
				return `30px`;
			} else {
				if (isTagsview) return `114px`;
				else return `80px`;
			}
		});
		// 设置 宽度小于 768，不支持操
		const setClientWidth = () => {
			const clientWidth = document.body.clientWidth;
			clientWidth < 768 ? (state.isShow = true) : (state.isShow = false);
		};
		// 加载计划任务信息
		const loadWorkableTask = (id: string) => {
			getWorkableTask({'id' : id}).then(res => {
				if(res.code === 0){
					state.workableTask = res.data;
					state.readonly = state.readonly ? true : (state.workableTask.status != '0' ? true : false);
				}
			});
		};
		// 左侧导航-数据初始化
		const initLeftNavList = async (id: string) => {
			state.leftNavList = leftNavList;
			state.jsplumbData = {
				nodeList: [],
				lineList: []
			};
			await getWorkableJsPlumbData({taskId: id}).then(res => {
				if(res.code == 0){
					if(res.data){
						state.jsplumbData.nodeList = res.data.nodeList || [];
						state.jsplumbData.lineList = res.data.lineList || [];
					}
				}
			})
		};
		// 左侧导航-初始化拖动
		const initSortable = () => {
			state.leftNavList.forEach((v, k) => {
				Sortable.create(document.getElementById(`left${k}`) as HTMLElement, {
					group: {
						name: 'vue-next-admin-1',
						pull: 'clone',
						put: false,
					},
					animation: 0,
					sort: false,
					draggable: '.workflow-left-item',
					forceFallback: true,
					onEnd: (e: any) => {
						if(state.readonly){
							ElMessage.warning('只读状态不允许操作');
							return;
						}
						const { name, icon, id, type, modelId } = e.clone.dataset;
						const { layerX, layerY, clientX, clientY } = e.originalEvent;
						const el = document.querySelector('#workflow-right') as HTMLElement;
						const { x, y, width, height } = el.getBoundingClientRect();
						if (clientX < x || clientX > width + x || clientY < y || y > y + height) {
							ElMessage.warning('请把节点拖入到画布中');
							return;
						}
						//校验开始节点，有且唯一一个
						for(let i = 0; i < state.jsplumbData.nodeList.length; i++){
							if(state.jsplumbData.nodeList[i].type == type && type == 'start'){
								ElMessage.warning('开始节点仅且只有一个');
								return;
							}
						}
						// 节点id（唯一）
						const nodeId = nanoid();
						// 处理节点数据
						const node = {
							taskId: state.workableTask.id,
							modelId,
							nodeId,
							left: `${layerX < 200 ? (layerX < 0 ? -layerX : layerX) : layerX - 200}px`,
							top: `${layerY < 0 ? -layerY : layerY + 60}px`,
							cssClass: type =='start' || type == 'end' ? 'workflow-right-circle-clone' : 'workflow-right-clone',
							name,
							icon,
							id,
							type,
						};
						// 右侧视图内容数组
						state.jsplumbData.nodeList.push(node);
						// 元素加载完毕时
						nextTick(() => {
							// 整个节点作为source或者target
							state.jsPlumb.makeSource(nodeId, state.jsplumbMakeSource);
							// // 整个节点作为source或者target
							state.jsPlumb.makeTarget(nodeId, state.jsplumbMakeTarget, jsplumbConnect);
							// 设置节点可以拖拽（此处为id值，非class）
							state.jsPlumb.draggable(nodeId, {
								containment: 'parent',
								stop: (el: any) => {
									state.jsplumbData.nodeList.forEach((v) => {
										if (v.nodeId === el.el.id) {
											// 节点x, y重新赋值，防止再次从左侧导航中拖拽节点时，x, y恢复默认
											v.left = `${el.pos[0]}px`;
											v.top = `${el.pos[1]}px`;
										}
									});
								},
							});
						});
					},
				});
			});
		};
		// 初始化 jsPlumb
		const initJsPlumb = () => {
			jsPlumb.ready(() => {
				state.jsPlumb = jsPlumb.getInstance({
					detachable: false,
					Container: 'workflow-right',
				});
				state.jsPlumb.fire('jsPlumbDemoLoaded', state.jsPlumb);
				// 导入默认配置
				state.jsPlumb.importDefaults(state.jsplumbDefaults);
				// 会使整个jsPlumb立即重绘。
				state.jsPlumb.setSuspendDrawing(false, true);
				// 初始化节点、线的链接
				initJsPlumbConnection();
				// 点击线
				state.jsPlumb.bind('click', (conn: any, originalEvent: MouseEvent) => {
					originalEvent.preventDefault();
					//取消所有连接的高亮颜色
					state.jsPlumb.select().setPaintStyle({stroke: '#E0E3E7', outlineStroke: "transparent"});
					//设置当前连接线高亮;
					conn.setPaintStyle({stroke: 'var(--el-color-primary)'});
				});
				// 点击线弹出右键菜单
				state.jsPlumb.bind('contextmenu', (conn: any, originalEvent: MouseEvent) => {
					originalEvent.preventDefault();
					const { sourceId, targetId } = conn;
					const { clientX, clientY } = originalEvent;
					state.dropdownLine.x = clientX;
					state.dropdownLine.y = clientY;
					//const node: any = state.jsplumbData.nodeList.find((v) => v.nodeId === targetId);
					const line: any = state.jsplumbData.lineList.find((v) => v.sourceId === sourceId && v.targetId === targetId);
					line.type = 'line';
					contextmenuLineRef.value.openContextmenu(line, conn);
				});
				// 连接之前
				state.jsPlumb.bind('beforeDrop', (conn: any) => {
					const { sourceId, targetId } = conn;
					if(sourceId == targetId || state.readonly){
						return false;
					}
					const lines = [];
					state.jsplumbData.lineList.forEach(line => {
						lines.push(line);
					});
					lines.push(conn);
					let verify = verifyJsplumbDataLine(lines, lines.length - 1);
					if(!verify){
						ElMessage.warning('不允许环形连接');
						return false;
					}
					const item = state.jsplumbData.lineList.find((v) => v.sourceId === sourceId && v.targetId === targetId);
					if (item) {
						ElMessage.warning('不允许重复连接');
						return false;
					} else {
						return true;
					}
				});
				// 连接时
				state.jsPlumb.bind('connection', (conn: any) => {
					const { sourceId, targetId } = conn;
					state.jsplumbData.lineList.push({
						taskId : state.workableTask.id,
						sourceId,
						targetId,
						label: '',
						expression: ''
					});
				});
				// 删除连接时回调函数
				state.jsPlumb.bind('connectionDetached', (conn: any) => {
					if(state.readonly){
						ElMessage.warning('只读状态不允许操作');
						return;
					}
					const { sourceId, targetId } = conn;
					state.jsplumbData.lineList = state.jsplumbData.lineList.filter((line) => {
						if (line.sourceId == sourceId && line.targetId == targetId) {
							if(line.id){
								delWorkableLine([line.id]).then();
							}
							return false;
						}
						return true;
					});
				});
			});
		};
		// 初始化节点、线的链接
		const initJsPlumbConnection = () => {
			// 节点
			state.jsplumbData.nodeList.forEach((v) => {
				// 整个节点作为source或者target
				state.jsPlumb.makeSource(v.nodeId, state.jsplumbMakeSource);
				// 整个节点作为source或者target
				state.jsPlumb.makeTarget(v.nodeId, state.jsplumbMakeTarget, jsplumbConnect);
				// 设置节点可以拖拽（此处为id值，非class）
				state.jsPlumb.draggable(v.nodeId, {
					containment: 'parent',
					stop: (el: any) => {
						state.jsplumbData.nodeList.forEach((v) => {
							if (v.nodeId === el.el.id) {
								// 节点x, y重新赋值，防止再次从左侧导航中拖拽节点时，x, y恢复默认
								v.left = `${el.pos[0]}px`;
								v.top = `${el.pos[1]}px`;
							}
						});
					},
				});
			});
			// 连接
			state.jsplumbData.lineList.forEach((v) => {
				state.jsPlumb.connect(
					{	
						id: v.id,
						taskId: v.taskId,
						source: v.sourceId,
						target: v.targetId,
						label: v.label,
						expression: v.expression,
					},
					state.jsplumbConnect
				);
			});
		};
		// 左侧导航-菜单标题点击
		const onTitleClick = (val: any) => {
			val.isOpen = !val.isOpen;
		};
		// 右侧内容区-当前项点击
		const onItemCloneClick = (k: number) => {
			state.jsPlumbNodeIndex = k;
		};
		// 右侧内容区-当前项双击
		const onItemCloneDblClick = (v: any, k: number) => {
			state.jsPlumbNodeIndex = k;
			v.label = '';
			let item: any = {};
			state.leftNavList.forEach((l) => {
				if (l.children) if (l.children.find((c: any) => c.modelId === v.modelId)) item = l.children.find((c: any) => c.modelId === v.modelId);
			});
			v.form = item.form;
			v.mark = item.mark;
			v.type = item.type ? item.type : 'node';
			drawerRef.value.open(v);
		};
		// 右侧内容区-当前项右键菜单点击
		const onContextmenu = (v: any, k: number, e: MouseEvent) => {
			state.jsPlumbNodeIndex = k;
			const { clientX, clientY } = e;
			state.dropdownNode.x = clientX;
			state.dropdownNode.y = clientY;
			v.label = '';
			let item: any = {};
			state.leftNavList.forEach((l) => {
				if (l.children) if (l.children.find((c: any) => c.modelId === v.modelId)) item = l.children.find((c: any) => c.modelId === v.modelId);
			});
			v.form = item.form;
			v.mark = item.mark;
			v.type = item.type ? item.type : 'node';
			contextmenuNodeRef.value.openContextmenu(v);
		};
		// 右侧内容区-当前项右键菜单点击回调(节点)
		const onCurrentNodeClick = (item: any) => {
			const { id, operate, nodeId } = item;
			if (operate === 0) {//删除节点
				if(state.readonly){
					ElMessage.warning('只读状态不允许操作');
					return;
				}
				if(id){
					delWorkableNode([id]).then();
				}
				const nodeIndex = state.jsplumbData.nodeList.findIndex((item) => item.nodeId === nodeId);
				state.jsplumbData.nodeList.splice(nodeIndex, 1);
				state.jsPlumb.removeAllEndpoints(nodeId);
				state.jsPlumbNodeIndex = null;
			} else if (operate === 1) {//编辑节点
				drawerRef.value.open(item);
			} else if (operate === 2) {//复制节点
				onCurrentNodeCopy(item);
			}
		};
		//复制节点
		const onCurrentNodeCopy = (item: any) => {
			const { taskId, modelId, left, top, cssClass, name, icon, type } = item;
			if(type == 'start' || type == 'line'){
				return;
			}
			// 节点id（唯一）
			const nodeId = nanoid();
			// 处理节点数据
			const node = {
				taskId,
				modelId,
				nodeId,
				left: (parseInt(left.replace('px', '')) + 50) + 'px',
				top: (parseInt(top.replace('px', '')) + 50) + 'px',
				cssClass,
				name,
				icon,
				type,
			};
			// 右侧视图内容数组
			state.jsplumbData.nodeList.push(node);
			// 元素加载完毕时
			nextTick(() => {
				// 整个节点作为source或者target
				state.jsPlumb.makeSource(nodeId, state.jsplumbMakeSource);
				// // 整个节点作为source或者target
				state.jsPlumb.makeTarget(nodeId, state.jsplumbMakeTarget, jsplumbConnect);
				// 设置节点可以拖拽（此处为id值，非class）
				state.jsPlumb.draggable(nodeId, {
					containment: 'parent',
					stop: (el: any) => {
						state.jsplumbData.nodeList.forEach((v) => {
							if (v.nodeId === el.el.id) {
								// 节点x, y重新赋值，防止再次从左侧导航中拖拽节点时，x, y恢复默认
								v.left = `${el.pos[0]}px`;
								v.top = `${el.pos[1]}px`;
							}
						});
					},
				});
			});
			
			getWorkableForm({taskId: taskId, nodeId: item.nodeId}).then(res => {
				if(res.code == 0){
					if(res.data && res.data.nodeForm){
						addWorkableForm({taskId: taskId, nodeId: nodeId, nodeForm: res.data.nodeForm}).then(() => {
							
						});
					}
				}
			})
		}
		// 右侧内容区-当前项右键菜单点击回调(线)
		const onCurrentLineClick = (item: any, conn: any) => {
			const { operate } = item;
			const { endpoints } = conn;
			const intercourse: any = [];
			endpoints.forEach((v: any) => {
				intercourse.push({
					id: v.element.id,
					innerText: v.element.innerText,
				});
			});
			item.contact = `${intercourse[0].innerText}(${intercourse[0].id}) => ${intercourse[1].innerText}(${intercourse[1].id})`;
			if (operate === 0){
				if(state.readonly){
					ElMessage.warning('只读状态不允许操作');
					return;
				}
				state.jsPlumb.deleteConnection(conn);
			} else if (operate === 1) {
				drawerRef.value.open(item, conn);
			}
		};
		// 设置线的 label
		const setLineLabel = (obj: any) => {
			const { sourceId, targetId, label, expression, id } = obj;
			const conn = state.jsPlumb.getConnections({
				source: sourceId,
				target: targetId,
			})[0];
			if(label){
				conn.setLabel(label);
			}
			if (!label || label === '') {
				conn.addClass('workflow-right-empty-label');
			} else {
				conn.removeClass('workflow-right-empty-label');
				conn.addClass('workflow-right-label');
			}
			state.jsplumbData.lineList.forEach((v) => {
				if (v.sourceId === sourceId && v.targetId === targetId){
					v.id = id;
					v.label = label;
					v.expression = expression;
				}
			});
		};
		// 设置节点内容
		const setNodeContent = (obj: any) => {
			const { nodeId, name, icon, id } = obj;
			// 设置节点 name 与 icon
			state.jsplumbData.nodeList.forEach((v) => {
				if (v.nodeId === nodeId) {
					v.id = id;
					v.name = name;
					v.icon = icon;
				}
			});
			// 重绘
			nextTick(() => {
				state.jsPlumb.setSuspendDrawing(false, true);
			});
		};
		// 顶部工具栏-当前项点击
		const onToolClick = (fnName: String) => {
			switch (fnName) {
				case 'help':
					onToolHelp();
					break;
				case 'download':
					onToolDownload();
					break;
				case 'run':
					onToolRun();
					break;
				case 'submit':
					onToolSubmit();
					break;
				case 'detail':
					onToolDetail();
					break;
				case 'copy':
					onToolCopy();
					break;
				case 'verify':
					onToolVerify();
					break;
				case 'del':
					onToolDel();
					break;
				case 'fullscreen':
					onToolFullscreen();
					break;
				case 'close':
					onToolClose();
					break;
			}
		};
		// 顶部工具栏-帮助
		const onToolHelp = () => {
			nextTick(() => {
				helpRef.value.open();
			});
		};
		// 顶部工具栏-下载
		const onToolDownload = () => {
			const href = 'data:text/json;charset=utf-8,' + encodeURIComponent(JSON.stringify(state.jsplumbData, null, '\t'));
			const aLink = document.createElement('a');
			aLink.setAttribute('href', href);
			aLink.setAttribute('download', `${state.workableTask.taskName}_工作流.json`);
			aLink.click();
			aLink.remove();
			ElMessage.success('下载成功');
		};
		// 顶部工具栏-运行
		const onToolRun = async () => {
			await onToolSubmit();
			if(state.workableTask.id){
				await runWorkableTask({ids: [state.workableTask.id], runmode: 0}).then((res) => {
					if(res.code === 0){
						ElMessage.success('运行成功');
						state.drawer.isShow = true;
						state.drawer.taskId = state.workableTask.id;
					}
				});
			}
		}
		// 顶部工具栏-提交
		const onToolSubmit = () => {
			if(state.readonly){
				ElMessage.warning('只读状态不允许操作');
				return;
			}
			//校验开始节点，有且唯一一个
			const starts = state.jsplumbData.nodeList.filter(line => {
				return line.type == 'start';
			});
			if(starts == null || starts.length != 1){
				ElMessage.warning('开始节点不能为空且仅且一个');
				return;
			}
			let verify = verifyJsplumbData();
			if(!verify){
				ElMessage.warning('流程图校验不通过，不允许环形连接');
				return;
			}
			saveWorkableJsPlumbData(state.jsplumbData).then(res => {
				if(res.code == 0){
					if(res.data){
						state.jsplumbData.nodeList = res.data.nodeList;
						state.jsplumbData.lineList = res.data.lineList;
					}
					ElMessage.success('保存成功');
				}
			})
		};
		// 顶部工具栏-详情
		const onToolDetail = () => {
			textRef.value.open(state.jsplumbData);
		}
		// 顶部工具栏-复制
		const onToolCopy = () => {
			copyText(JSON.stringify(state.jsplumbData));
		};
		// 顶部工具栏-检查
		const onToolVerify = () => {
			//校验开始节点，有且唯一一个
			const starts = state.jsplumbData.nodeList.filter(line => {
				return line.type == 'start';
			});
			if(starts == null || starts.length != 1){
				ElMessage.warning('开始节点不能为空且仅且一个');
				return;
			}
			let verify = verifyJsplumbData();
			if(!verify){
				ElMessage.warning('流程图校验不通过，不允许环形连接');
				return;
			}
			ElMessage.success('校验通过');
		}
		const verifyJsplumbData = () => {
			const lines = [];
			state.jsplumbData.lineList.forEach(line => {
				lines.push(line);
			});
			if(lines.length <= 0){
				return true;
			}
			for(let i = 0; i < lines.length; i++){
				let verify = verifyJsplumbDataLine(lines, i);
				if(!verify){
					return false;
				}
			}
			return true;
		}
		const verifyJsplumbDataLine = (lines, index) => {
			const line = lines[index];
			const targets = getTargetLinesByTargetId(line.targetId, lines);
			return verifyJsplumbDataTargets(line, targets, lines);
		}
		const verifyJsplumbDataTargets = (line, targets, lines) => {
			const sources = getTargetLinesBySourceId(line.sourceId, lines);
			if(sources.length <= 1){
				return true;
			}
			if(targets.length > 0){
				for(let i = 0; i < targets.length; i++){
					if(line.sourceId == targets[i].targetId){
						return false;
					}
					if(line.sourceId == targets[i].targetId && line.targetId == targets[i].sourceId){
						return false;
					}
					const target2s = getTargetLinesByTargetId(targets[i].targetId, lines);
					let verify = verifyJsplumbDataTargets(line, target2s, lines);
					if(!verify){
						return false;
					}
				}
			}
			return true;
		}
		const getTargetLinesBySourceId = (sourceId, lines) => {
			const sources = [];
			lines.forEach(line => {
				if(line.sourceId == sourceId){
					sources.push(line);
				}
			});
			return sources;
		}
		const getTargetLinesByTargetId = (targetId, lines) => {
			const targets = [];
			lines.forEach(line => {
				if(line.sourceId == targetId){
					targets.push(line);
				}
			});
			return targets;
		}
		// 顶部工具栏-删除
		const onToolDel = () => {
			if(state.readonly){
				ElMessage.warning('只读状态不允许操作');
				return;
			}
			ElMessageBox.confirm('此操作将清空画布，是否继续？', '提示', {
				confirmButtonText: '清空',
				cancelButtonText: '取消',
			}).then(() => {
				let ids = [];
				state.jsplumbData.nodeList.forEach((v) => {
					if(v.id){
						ids.push(v.id);
					}
					state.jsPlumb.removeAllEndpoints(v.nodeId);
				});
				if(ids.length > 0){
					delWorkableNode(ids).then();
				}
				nextTick(() => {
					state.jsplumbData = {
						nodeList: [],
						lineList: [],
					};
					ElMessage.success('清空画布成功');
				});
			})
			.catch(() => {});
		};
		// 顶部工具栏-全屏
		const onToolFullscreen = () => {
			store.dispatch('tagsViewRoutes/setCurrenFullscreen', true);
		};
		// 顶部工具栏-关闭
		const onToolClose = () => {
			proxy.mittBus.emit('onCurrentContextmenuClick', Object.assign({}, { contextMenuClickId: 1, ...route }));
		}
		// 页面加载时
		onMounted(async () => {
			state.traceId = route.query.traceId;
			state.readonly = state.traceId ? true : false;
			await loadWorkableTask(route.query.id);
			await initLeftNavList(route.query.id);
			initSortable();
			initJsPlumb();
			setClientWidth();
			window.addEventListener('resize', setClientWidth);
		});
		// 页面卸载时
		onUnmounted(() => {
			window.removeEventListener('resize', setClientWidth);
		});
		return {
			setViewHeight,
			setClientWidth,
			setLineLabel,
			setNodeContent,
			onTitleClick,
			onItemCloneClick,
			onItemCloneDblClick,
			onContextmenu,
			onCurrentNodeClick,
			onCurrentLineClick,
			contextmenuNodeRef,
			contextmenuLineRef,
			drawerRef,
			helpRef,
			textRef,
			onToolClick,
			...toRefs(state),
		};
	},
});
</script>

<style scoped lang="scss">
.workflow-container {
	position: relative;
	.workflow {
		display: flex;
		height: 100%;
		width: 100%;
		flex-direction: column;
		.workflow-content {
			display: flex;
			height: calc(100% - 35px);
			#workflow-left {
				width: 220px;
				height: 100%;
				border-right: 1px solid var(--el-border-color-light, #ebeef5);
				::v-deep(.el-collapse-item__content) {
					padding-bottom: 0;
				}
				.workflow-left-title {
					height: 50px;
					display: flex;
					align-items: center;
					padding: 0 10px;
					border-top: 1px solid var(--el-border-color-light, #ebeef5);
					color: var(--el-text-color-primary);
					cursor: default;
					span {
						flex: 1;
					}
				}
				.workflow-left-item {
					display: inline-block;
					width: calc(50% - 15px);
					position: relative;
					cursor: move;
					margin: 0 0 10px 10px;
					.workflow-left-item-icon {
						height: 35px;
						display: flex;
						align-items: center;
						transition: all 0.3s ease;
						padding: 5px 10px;
						border: 1px dashed transparent;
						background: var(--next-bg-color);
						border-radius: 3px;
						i,
						.name {
							color: var(--el-text-color-secondary);
							transition: all 0.3s ease;
							white-space: nowrap;
							text-overflow: ellipsis;
							overflow: hidden;
						}
						&:hover {
							transition: all 0.3s ease;
							border: 1px dashed var(--el-color-primary);
							background: var(--el-color-primary-light-9);
							border-radius: 5px;
							i,
							.name {
								transition: all 0.3s ease;
								color: var(--el-color-primary);
							}
						}
					}
				}
				& .workflow-left-id:first-of-type {
					.workflow-left-title {
						border-top: none;
					}
				}
			}
			#workflow-right {
				flex: 1;
				position: relative;
				overflow: hidden;
				height: 100%;
				background-image: linear-gradient(90deg, rgb(156 214 255 / 15%) 10%, rgba(0, 0, 0, 0) 10%),
					linear-gradient(rgb(156 214 255 / 15%) 10%, rgba(0, 0, 0, 0) 10%);
				background-size: 10px 10px;
				.workflow-right-clone {
					position: absolute;
					.workflow-right-box {
						height: 35px;
						align-items: center;
						color: var(--el-text-color-secondary);
						padding: 0 10px;
						border-radius: 3px;
						cursor: move;
						transition: all 0.3s ease;
						min-width: 94.5px;
						background: var(--el-color-white);
						border: 1px solid var(--el-border-color-light, #ebeef5);
						.workflow-left-item-icon {
							display: flex;
							align-items: center;
							height: 35px;
						}
						&:hover {
							border: 1px dashed var(--el-color-primary);
							background: var(--el-color-primary-light-9);
							transition: all 0.3s ease;
							color: var(--el-color-primary);
							i {
								cursor: Crosshair;
							}
						}
					}
					.workflow-right-active {
						border: 1px dashed var(--el-color-primary);
						background: var(--el-color-primary-light-9);
						color: var(--el-color-primary);
					}
				}
				.workflow-right-circle-clone {
					position: absolute;
					.workflow-right-box {
						height: 45px;
						align-items: center;
						color: var(--el-text-color-secondary);
						padding: 0 5px;
						border-radius: 50%;
						cursor: move;
						transition: all 0.3s ease;
						width: 45px;
						background: var(--el-color-white);
						border: 1px solid var(--el-border-color-light, #ebeef5);
						.workflow-left-item-icon {
							display: flex;
							align-items: center;
							height: 45px;
						}
						&:hover {
							border: 1px dashed var(--el-color-primary);
							background: var(--el-color-primary-light-9);
							transition: all 0.3s ease;
							color: var(--el-color-primary);
							i {
								cursor: Crosshair;
							}
						}
					}
					.workflow-right-active {
						border: 1px dashed var(--el-color-primary);
						background: var(--el-color-primary-light-9);
						color: var(--el-color-primary);
					}
				}
				::v-deep(.jtk-overlay):not(.aLabel) {
					padding: 4px 10px;
					border: 1px solid var(--el-border-color-light, #ebeef5) !important;
					color: var(--el-text-color-secondary) !important;
					background: var(--el-color-white) !important;
					border-radius: 3px;
					font-size: 10px;
				}
				::v-deep(.jtk-overlay.workflow-right-empty-label) {
					display: none;
				}
			}
		}
	}
	.workflow-mask {
		position: absolute;
		top: 0;
		right: 0;
		bottom: 0;
		left: 0;
		&::after {
			content: '手机版不支持 jsPlumb 操作';
			position: absolute;
			top: 0;
			right: 0;
			bottom: 0;
			left: 0;
			z-index: 1;
			background: rgba(255, 255, 255, 0.9);
			color: #666666;
			display: flex;
			align-items: center;
			justify-content: center;
		}
	}
}
</style>
